/* from EOPS, lots change */

#include "my_mem.h"
#include <stdbool.h>
#include <stdio.h>

#define MIN_BLK_SIZE	(16+sizeof(MemBlkItem))
#define MEM_BLK_ALIGN	16
#define MEM_BLK_MASK	(MEM_BLK_ALIGN - 1)
#define MEM_BLK_MARK	0x4259	// BY

uint32_t HEAP_BASE = 0, HEAP_SIZE = 0;

//! the memory block control bytes
typedef struct MemBlkItem_tag
{
	uint16_t mark;		//!< mark for memory over range check
	bool     used;		//!< if block are allocated
	struct MemBlkItem_tag * prev;	//!< pointer to previous memory block
	struct MemBlkItem_tag * next;	//!< pointer to next memory block
	uint32_t size;		//!< block size, extra control bytes included
} MemBlkItem;

//! Initialize memory management, whole memory as a free block
void MemInit(uint32_t baseAddr, uint32_t size)
{
	HEAP_BASE = baseAddr;
	HEAP_SIZE = size;
	MemBlkItem * item = (MemBlkItem *)HEAP_BASE;

	item->prev = item;
	item->next = item;
	item->size = HEAP_SIZE;
	item->used = false;
	item->mark = MEM_BLK_MARK;

	//MEM_PRINTF("baseAddr 0x%x size %d \n",baseAddr,size);
}

void * MemMalloc(uint32_t size)
{
	MemBlkItem * item = (MemBlkItem *)HEAP_BASE;

	size += sizeof(MemBlkItem);		// include control bytes
	size = (size + MEM_BLK_MASK) & ~MEM_BLK_MASK;	// and should up align to bound
	do
	{
		if (!item->used && (item->size >= size))
		{
			if (item->size > (size + MIN_BLK_SIZE))	// split the memory if block large enough
			{
				// item -> newItem -> nextItem
				MemBlkItem * nextItem = item->next;
				MemBlkItem * newItem = (MemBlkItem *)((uint32_t)item + size);
				nextItem->prev = newItem;
				newItem->next = nextItem;
				newItem->prev = item;
				item->next = newItem;

				newItem->used = false;
				newItem->size = item->size - size;
				newItem->mark = MEM_BLK_MARK;

				item->size = size;
			}
			item->used = true;
			break;
		}
		item = item->next;
	}while (item != (MemBlkItem *)HEAP_BASE);

	if(item == NULL)
	{
		printf("mem error\n");
		MemDump("err");
	}
	
	return item ? item + 1 : NULL;	// return pointer should pointer to user
}

void MemFree(void * p)
{
	if (p == NULL) return;	// pass in NULL is OK

	MemBlkItem * item = (MemBlkItem *)((uint32_t)p - sizeof(MemBlkItem));
	if (item->mark != MEM_BLK_MARK) return ;
	if (!item->used) return ;

	MemBlkItem * prevItem = item->prev;
	MemBlkItem * nextItem = item->next;

	// if not first item, should look forward for adjance item combine
	if ((item != (MemBlkItem *)HEAP_BASE) && !prevItem->used)
	{
		prevItem->next = item->next;
		nextItem->prev = prevItem;
		prevItem->size += item->size;
		item->mark = 0;		// clear mark for it not a item control any more
		item = prevItem;
	}

	// if next item is not first item, should combine adjance item
	if ((nextItem != (MemBlkItem *)HEAP_BASE) && !nextItem->used)
	{
		item->next = nextItem->next;
		item->next->prev = item;
		item->size += nextItem->size;
		nextItem->mark = 0;		// clear mark for it not a item control any more
	}
	item->used = false;

	//return 0;
}

void MemDump(const char * msg)
{
	MemBlkItem * item = (MemBlkItem *)HEAP_BASE;
	printf("===========%s=============== \n", msg);
	printf("Item Used     Base   Size\n");
	uint32_t cnt = 0;
	do
	{
		printf("%4d %4c %8X %6X\n", cnt++, item->used ? '*' : ' ', item, item->size);
		item = item->next;
	}
	while (item != (MemBlkItem *)HEAP_BASE);
}

